import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder';
import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial';
import { Color3 } from '@babylonjs/core/Maths/math.color';
import { CreateHandleTo, IHandle, RemoveHandle } from '../data/data-handle';
import { AnimationFrame } from '../animation/animation-frame';
import { Scene } from '@babylonjs/core/scene';
import { Helper } from '../tool/helper';
import { Camera } from '@babylonjs/core/Cameras/camera';


/**
 * 相机淡入淡出遮罩
 */
class CameraMask {

    private static _scene?: Scene;
    private static _camera?: Camera;
    private static _enabled = false;
    private static _ts = new Float32Array(2);
    private static _req = {};
    private static _startHandles: IHandle<number>[] = [];
    private static _progressHandles: IHandle<number>[] = [];
    private static _endHandles: IHandle<number>[] = [];

    public static speed = 0.01;
    public static mask?: AbstractMesh;
    public static sizeRatio = 2;

    /**
     * 添加渐变开始回调函数对象
     * @param func 回调函数
     * @returns 回调函数对象
     */
    public static onStart(func: { (progress: number): void }): IHandle<number> {
        return CreateHandleTo(func, this._startHandles);
    }

    /**
     * 移除渐变开始回调函数对象
     * @param handle 回调函数对象或其id
     */
    public static removeStartHandle(handle: number | IHandle<number>): void {
        RemoveHandle(handle, this._startHandles);
    }

    /**
     * 添加渐变中回调函数对象
     * @param func 回调函数
     * @returns 回调函数对象
     */
    public static onProgress(func: { (progress: number): void }): IHandle<number> {
        return CreateHandleTo(func, this._progressHandles);
    }

    /**
     * 移除渐变中回调函数对象
     * @param handle 回调函数对象或其id
     */
    public static removeProgressHandle(handle: number | IHandle<number>): void {
        RemoveHandle(handle, this._progressHandles);
    }

    /**
     * 添加渐变结束调函数对象
     * @param func 回调函数
     * @returns 回调函数对象
     */
    public static onEnd(func: { (progress: number): void }): IHandle<number> {
        return CreateHandleTo(func, this._endHandles);
    }

    /**
     * 移除渐变结束回调函数对象
     * @param handle 回调函数对象或其id
     */
    public static removeEndHandle(handle: number | IHandle<number>): void {
        RemoveHandle(handle, this._endHandles);
    }

    /**
     * 设置应用到的场景
     */
    public static set scene(scene: Scene) {
        this._scene = scene;
        if (!this.mask) this.mask = this.generateMaskPlane();
        this.attach();
        this._scene.onActiveCameraChanged.add(() => {
            if (!Helper.isCameraGeneral(this._scene?.activeCamera)) return;
            if (this._camera && this._camera.uniqueId === this._scene?.activeCamera?.uniqueId) return;
            
            if (!this.mask) this.mask = this.generateMaskPlane();
            this.attach();
        });
    }

    /**
     * 设置是否启用
     */
    public static set enabled(enabled: boolean) {
        this._enabled = enabled;
        if (this.mask) this.mask.isVisible = this._enabled;
    }

    /**
     * 开始渐变
     * @param toDark 是否淡入（否则淡出）
     * @param endCallback 结束时的回调函数
     */
    public static transition(toDark: boolean, endCallback?: { (): void }) {
        const ts = this._ts;
        if (AnimationFrame.isRunning(this._req)) {
            AnimationFrame.cancel(this._req);
            this._endHandles.forEach(handle => handle.callback(ts[1]));
        }
        this._startHandles.forEach(handle => handle.callback(ts[1]));
        const spd = this.speed;
        ts[0] = toDark ? spd : -spd;
        const _this = CameraMask;
        AnimationFrame.request(() => {
            ts[1] += ts[0];
            ts[1] = Math.max(Math.min(ts[1], 1), 0);
            _this._progressHandles.forEach(handle => handle.callback(ts[1]));
            if (_this.mask) _this.mask.visibility = ts[1];
            if (ts[1] === 0 || ts[1] === 1) {
                _this._endHandles.forEach(handle => handle.callback(ts[1]));
                AnimationFrame.cancel(_this._req);
                if (endCallback) endCallback();
            }
        }, 16, 200, this._req);
    }

    /**
     * 重置
     */
    public static reset(): void {
        AnimationFrame.cancel(this._req);
        this.enabled = false;
        this._ts[1] = 0;
        if (this.mask) this.mask.visibility = 0;
    }

    /**
     * 向活动相机添加遮罩
     */
    private static attach(): void {
        const mask = this.mask!;
        this._camera = this._scene?.activeCamera!;
        mask.parent = this._camera;
        const s = this._camera.minZ * this.sizeRatio;
        mask.scaling.set(s, s, s);
        mask.position.set(0, 0, 0);
    }

    /**
     * 创建遮罩平面
     * @returns 遮罩平面
     */
    private static generateMaskPlane(): AbstractMesh {
        const scene = this._scene;
        const p = MeshBuilder.CreateSphere('camera-mask-mesh', { diameter: 2 }, scene);
        const m = new StandardMaterial('camera-mask-material', scene);
        p.material = m;
        m.backFaceCulling = false;
        m.diffuseColor = new Color3(0, 0, 0);
        m.disableLighting = true;
        p.isBlocker = false;
        p.isPickable = false;
        p.isVisible = this._enabled;
        p.visibility = 0;
        return p;
    }

}


export { CameraMask };